/**
 * 
 */
package com.google.code.cisx.wfe.events;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Manages Events, Listeners and Event dispatches to appropriate listeners.
 * <p>
 * This is similar to a notification bus.
 * 
 * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
 * @since 1.0
 * @version $Id: EventManager.java 258 2008-11-29 06:23:51Z rahul.thakur.xdev $
 * @see <a href='http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/6635205f5ce8208a/2cffc8ca1d81471f?lnk=gst&q=Event+manager#2cffc8ca1d814
 *      7 1 f ' > Event Manager</a>
 */
public class EventManager {
    /**
     * Singleton {@link EventManager} instance.
     */
    private static final EventManager instance = new EventManager();

    /**
     * Collection of {@link TypedEventListener}s registered for different
     * <em>types</em> of {@link TypedEvent}s.
     */
    private static final Map<EventType, List<TypedEventListener<TypedEvent>>> listeners = new HashMap<EventType, List<TypedEventListener<TypedEvent>>>();

    /**
     * Returns a singleton instance of {@link EventManager}.
     * 
     * @return
     */
    public static EventManager getInstance() {
        return instance;
    }

    /**
     * Adds a Registered Listener.
     * 
     * @param <T>
     *            extension of {@link TypedEventListener}
     * @param type
     *            type of {@link TypedEvent}.
     * @param listener
     *            {@link TypedEventListener} to add.
     */
    @SuppressWarnings("unchecked")
    public <E extends TypedEvent, T extends TypedEventListener<E>> void addEventListener(
            EventType type, T listener) {
        List<TypedEventListener<TypedEvent>> typedListeners = null;
        if (!listeners.containsKey(type)) {
            listeners
                    .put(
                            type,
                            new EventListenersCollection<TypedEventListener<TypedEvent>>());

            System.out.println("Added listener for event type: "
                    + type.asString());
        }
        typedListeners = listeners.get(type);
        typedListeners.add((TypedEventListener<TypedEvent>) listener);
    }

    /**
     * Removes a Registered listener.
     * 
     * @param <T>
     *            extension of {@link TypedEventListener}
     * @param type
     *            type of {@link TypedEvent}.
     * @param listener
     *            {@link TypedEventListener} to remove.
     */
    public <E extends TypedEvent, T extends TypedEventListener<E>> void removeEventListener(
            EventType type, T listener) {
        List<TypedEventListener<TypedEvent>> typedListeners = null;
        if (listeners.containsKey(type)) {
            typedListeners = listeners.get(type);
            typedListeners.remove(listener);
        }
    }

    /**
     * Dispatches Event notification to {@link TypedEventListener}s registered
     * for that particular type of Event.
     * 
     * @param <E>
     *            extension of {@link TypedEvent}.
     * @param event
     *            event to handle
     */
    public <E extends TypedEvent> void dispatchEvent(final E event) {
        if (listeners.containsKey(event.getType())) {
            final EventListenersCollection<TypedEventListener<TypedEvent>> typeListeners = (EventListenersCollection<TypedEventListener<TypedEvent>>) listeners
                    .get(event.getType());
            // TODO: DO some action
            System.out.println("Dispatching event to the Event handler");
        }
    }

    /**
     * Collection of Event Listeners registered for a <em>type</em> of event.
     * 
     * @author <a href='mailto:rahul.thakur.xdev@gmail.com'>Rahul Thakur</a>
     * @since 1.0
     * @param <T>
     *            {@link TypedEventListener}
     */
    static class EventListenersCollection<T extends TypedEventListener<TypedEvent>>
            extends ArrayList<T> {
        private static final long serialVersionUID = 3425680778337656685L;

        public void onEvent(TypedEvent event) {
            for (Iterator<T> it = iterator(); it.hasNext();) {
                T listener = it.next();
                listener.onEvent(event);
            }
        }
    }

}
